# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re, copy
import numpy as np
from hysop.tools.htypes import to_list, check_instance, first_not_None
from hysop.testsenv import __HAS_OPENCL_BACKEND__
if __HAS_OPENCL_BACKEND__:
import hysop.backend.device.opencl.opencl_types
from hysop.backend.device.codegen.base.utils import VarDict
if __HAS_OPENCL_BACKEND__:
from hysop.backend.device.opencl.opencl_types import TypeGen, OpenClTypeGen
# opencl extras
from hysop.backend.device.opencl.opencl_types import cl_type_to_dtype
_ctype_to_dtype = {
"bool": np.bool_,
"char": np.int8,
"short": np.int16,
"int": np.int32,
"long": np.int64,
"long int": np.int64,
"long long int": np.int64,
"signed char": np.int8,
"signed short": np.int16,
"signed int": np.int32,
"signed long": np.int64,
"signed long int": np.int64,
"signed long long int": np.int64,
"uchar": np.uint8,
"ushort": np.uint16,
"uint": np.uint32,
"ulong": np.uint64,
"unsigned char": np.uint8,
"unsigned short": np.uint16,
"unsigned int": np.uint32,
"unsigned long int": np.uint64,
"unsigned long long int": np.uint64,
"size_t": np.uint64, # warning: host and/or device can be 32bits, do not use in kernel arguments
"ptrdiff_t": np.int64, # warning: host and/or device can be 32bits, do not use in kernel arguments
"half": np.float16,
"float": np.float32,
"double": np.float64,
"long double": np.longdouble,
"float2": np.complex64,
"double2": np.complex128,
}
_dtype_to_ctype = {
np.bool_: "bool",
np.int8: "char",
np.int16: "short",
np.int32: "int",
np.int64: "long",
np.uint8: "uchar",
np.uint16: "ushort",
np.uint32: "uint",
np.uint64: "ulong",
np.float16: "half",
np.float32: "float",
np.float64: "double",
np.longdouble: "long double",
np.complex64: "float2",
np.complex128: "double2",
}
[docs]
def dtype_to_ctype(dtype):
from hysop.tools.numerics import get_dtype
dtype = get_dtype(dtype)
if dtype not in _dtype_to_ctype.keys():
msg = f"Unknown dtype {dtype}."
raise ValueError(msg)
else:
return _dtype_to_ctype[dtype]
[docs]
def ctype_to_dtype(ctype):
if ctype not in _ctype_to_dtype.keys():
from hysop.backend.device.opencl.opencl_types import np_dtype
return np_dtype(ctype)
else:
return _ctype_to_dtype[ctype]
[docs]
def register_ctype_dtype(ctype, dtype):
if dtype not in _dtype_to_ctype.keys():
_dtype_to_ctype[dtype] = ctype
if ctype not in _ctype_to_dtype.keys():
_ctype_to_dtype[ctype] = dtype
[docs]
class CodegenVariable:
def __init__(
self,
name,
ctype,
typegen,
storage=None,
const=False,
volatile=False,
static=False,
add_impl_const=False,
nl=None,
align=None,
ptr=False,
ptr_restrict=None,
ptr_volatile=None,
ptr_const=None,
value=None,
svalue=None,
init=None,
symbolic_mode=False,
struct_var=None,
):
check_instance(typegen, TypeGen)
check_instance(name, str)
check_instance(ctype, str)
check_instance(storage, str, allow_none=True)
assert len(name) > 0
assert len(ctype) > 0
assert (storage is None) or len(ctype) > 0
check_instance(const, bool)
check_instance(volatile, bool)
check_instance(add_impl_const, bool)
check_instance(nl, bool, allow_none=True)
check_instance(align, bool, allow_none=True)
self.name = name
self.ctype = ctype
self.typegen = typegen
self.const = const
self.volatile = volatile
self.add_impl_const = add_impl_const
self.storage = storage
self.align = align
self.static = static
self.declared = False
self.nl = nl if (nl is not None) else (storage is not None)
self.struct_var = struct_var
self.symbolic_mode = symbolic_mode
# pointer
if isinstance(ptr, bool):
is_ptr = ptr
ptr_level = int(ptr)
else:
check_instance(ptr, int)
is_ptr = True
ptr_level = ptr
del ptr
if is_ptr:
ptr_restrict = [] if (ptr_restrict is None) else to_list(ptr_restrict)
ptr_const = [] if (ptr_const is None) else to_list(ptr_const)
ptr_volatile = [] if (ptr_volatile is None) else to_list(ptr_volatile)
_len = max(len(ptr_restrict), len(ptr_const), len(ptr_volatile))
assert _len <= ptr_level
ptr_restrict = np.asarray(
ptr_restrict + [False] * (ptr_level - len(ptr_restrict))
)
ptr_volatile = np.asarray(
ptr_volatile + [False] * (ptr_level - len(ptr_volatile))
)
ptr_const = np.asarray(ptr_const + [False] * (ptr_level - len(ptr_const)))
else:
assert ptr_restrict is None
assert ptr_volatile is None
assert ptr_const is None
self.is_ptr = is_ptr
self.ptr_level = ptr_level
self.ptr_restrict = ptr_restrict
self.ptr_const = ptr_const
self.ptr_volatile = ptr_volatile
# value and svalue
if value is None:
value = None
elif np.isscalar(value):
if ctype == "fbtype":
dtype = self.typegen.floatn(1)
else:
dtype = ctype_to_dtype(ctype)
value = np.asarray([value], dtype=dtype)[0]
elif value.__class__ in [list, tuple]:
dtype = ctype_to_dtype(ctype)
try:
value = np.asarray(value, dtype=dtype)
except:
value = np.asarray(value, dtype=object)
elif isinstance(value, np.ndarray):
try:
dtype = ctype_to_dtype(ctype)
value = np.asarray(value, dtype=dtype).copy()
except:
value = value.copy()
else:
pass
self.value = value
self.svalue = svalue
self.init = init
# check
if add_impl_const:
if not is_ptr:
if const:
msg = "Variable {} is const and add_impl_const has been specified!"
msg = msg.format(name)
raise ValueError(msg)
elif len(ptr_const) > 0:
if ptr_const[-1]:
msg = "Variable {} has ptr_const[-1]=True and add_impl_const has been specified!"
msg = msg.format(name)
raise ValueError(msg)
[docs]
def nv_replace(self, old, new):
name = self.name.replace(old, new)
return self.newvar(name)
[docs]
def copy(self):
return self.newvar(name=self.name)
@property
def dim(self):
return self.typegen.components(self.ctype)
@property
def basetype(self):
return self.typegen.basetype(self.ctype)
[docs]
def newvar(
self,
name,
nl=False,
typegen=None,
storage=None,
value=None,
svalue=None,
init=None,
ctype=None,
const=None,
volatile=None,
add_impl_const=None,
ptr=None,
ptr_restrict=None,
ptr_volatile=None,
ptr_const=None,
cls=None,
**kwds,
):
ctype = self.ctype if (ctype is None) else ctype
const = self.const if (const is None) else const
volatile = self.volatile if (volatile is None) else volatile
add_impl_const = (
self.add_impl_const if (add_impl_const is None) else add_impl_const
)
storage = self.storage if (storage is None) else storage
typegen = self.typegen if (typegen is None) else typegen
cls = self.__class__ if (cls is None) else cls
if ptr is None:
ptr = self.ptr_level if (ptr is None) else ptr
ptr_const = self.ptr_const if (ptr_const is None) else ptr_const
ptr_volatile = self.ptr_volatile if (ptr_volatile is None) else ptr_volatile
ptr_restrict = self.ptr_restrict if (ptr_restrict is None) else ptr_restrict
return cls(
name=name,
nl=nl,
value=value,
svalue=svalue,
init=init,
ctype=ctype,
storage=storage,
const=const,
volatile=volatile,
add_impl_const=add_impl_const,
ptr=ptr,
ptr_restrict=ptr_restrict,
ptr_volatile=ptr_volatile,
ptr_const=ptr_const,
typegen=typegen,
**kwds,
)
[docs]
def alias(self, varname, **kwds):
if self.symbolic_mode:
init = self.access_prefix(access=False)
return self.newvar(name=varname, init=init, **kwds)
else:
value = self.value
return self.newvar(name=varname, value=value, init=None, **kwds)
[docs]
def pointer_alias(self, name, ctype, **kargs):
handle = self.newvar(
name=name,
ctype=ctype,
init="({})({})".format(self.full_ctype(cast=True, ctype=ctype), self),
**kargs,
)
return handle
[docs]
def pointer(
self,
name,
ptr_level,
ptr_const=None,
ptr_volatile=None,
ptr_restrict=None,
add_impl_const=False,
with_init=True,
**kargs,
):
ptr_const = [False] * ptr_level if (ptr_const is None) else to_list(ptr_const)
ptr_volatile = (
[False] * ptr_level if (ptr_volatile is None) else to_list(ptr_volatile)
)
ptr_restrict = (
[False] * ptr_level if (ptr_restrict is None) else to_list(ptr_restrict)
)
assert ptr_level > 0
assert len(ptr_const) == ptr_level
assert len(ptr_volatile) == ptr_level
assert len(ptr_restrict) == ptr_level
if self.is_ptr:
ptr_level = self.ptr_level + ptr_level
ptr_const = self.ptr_const + ptr_const
ptr_volatile = self.ptr_volatile + ptr_volatile
ptr_restrict = self.ptr_restrict + ptr_restrict
if with_init:
init = f"&{self.name}"
else:
init = None
return self.newvar(
name=name,
ptr=ptr_level,
ptr_const=ptr_const,
ptr_volatile=ptr_volatile,
ptr_restrict=ptr_restrict,
init=init,
add_impl_const=add_impl_const,
**kargs,
)
[docs]
def base_ctype(
self,
storage=None,
ctype=None,
const=None,
volatile=None,
impl=True,
align=None,
add_impl_const=None,
static=None,
):
align = first_not_None(align, self.align, False)
storage = self.storage if (storage is None) else storage
ctype = self.ctype if (ctype is None) else ctype
volatile = self.volatile if (volatile is None) else volatile
static = self.static if (static is None) else static
static |= (storage is not None) and ("__constant" in storage)
if const is None:
const = self.const
if impl and (not self.is_ptr) and (not const):
const = (
self.add_impl_const if (add_impl_const is None) else add_impl_const
)
const |= (storage is not None) and ("__constant" in storage)
base_ctype = "{storage}${static}${const}${volatile}${ctype}".format(
storage=f"{storage} " if (storage is not None) else "",
static="static " if static else "",
const="const " if const else "",
volatile="volatile " if volatile else "",
ctype=ctype + " ",
)
if not align:
base_ctype = base_ctype.replace("$", "")
return base_ctype.strip()
[docs]
def ptr_ctype(self, impl=True, add_impl_const=None, cast=False):
if self.is_ptr:
ptrs = []
add_impl_const = (
self.add_impl_const if (add_impl_const is None) else add_impl_const
)
for i, (c, v, r) in enumerate(
zip(self.ptr_const, self.ptr_volatile, self.ptr_restrict)
):
if i == self.ptr_level - 1:
c = c or (impl and add_impl_const)
ptr = " $*{const}${volatile}${restrict}".format(
const="const " if (c and not cast) else "",
volatile="volatile " if (v and not cast) else "",
restrict="restrict " if (r and not cast) else "",
)
ptrs.append(ptr)
ptr_ctype = "".join(ptrs)
else:
ptr_ctype = ""
return ptr_ctype
[docs]
def full_ctype(
self,
storage=None,
ctype=None,
const=None,
volatile=None,
impl=True,
multidecl=False,
align=None,
cast=False,
add_impl_const=None,
**kwds,
):
align = first_not_None(align, self.align, False)
if multidecl:
base_ctype = ""
else:
base_ctype = self.base_ctype(
storage,
ctype,
const,
volatile,
impl,
align,
add_impl_const=add_impl_const,
**kwds,
)
if len(base_ctype) == 0:
msg = f"Failed to get base ctype in {self.__class__}."
raise RuntimeError(msg)
ptr_ctype = self.ptr_ctype(impl=impl, add_impl_const=add_impl_const, cast=cast)
full_ctype = f"{base_ctype}{ptr_ctype}"
if not align:
full_ctype = full_ctype.replace("$", "")
else:
full_ctype = full_ctype.replace("$ ", "$")
return full_ctype.strip()
[docs]
def argument(self, impl, nl=None, name=None, **kargs):
name = self.name if (name is None) else name
nl = self.nl if (nl is None) else nl
return "{} {name}{nl}".format(
self.full_ctype(impl=impl, **kargs), name=name, nl="\n" if self.nl else ""
)
[docs]
def is_symbolic(self):
return (self.value is None) or self.symbolic_mode
[docs]
def in_struct(self):
return self.struct_var is not None
[docs]
def known(self):
return self.value is not None
[docs]
def force_symbolic(self, force=True):
if force is None:
return
self.symbolic_mode = force
[docs]
def set_value(self, val):
assert not self.known(), f"Value was already set in variable {self.name}!"
self.value = val
[docs]
def sval(self, symbolic=None):
symbolic = self.is_symbolic() if (symbolic is None) else symbolic
if symbolic:
return self.access_prefix(access=False)
else:
if self.svalue is not None:
return self.svalue
elif isinstance(self.value, bool):
return "true" if self.value else "false"
elif self.value is not None:
return self.typegen.dump(self.value)
else:
msg = "value and svalue are not defined."
raise RuntimeError(msg)
[docs]
def access_prefix(self, access):
acc = ""
if self.in_struct():
acc += self.struct_var.access_prefix(access=True)
acc += self.name
if access:
acc += "->" if self.is_ptr else "."
return acc
[docs]
def decl_name(self):
return self.name
[docs]
def declare(
self,
codegen=None,
align=None,
multidecl=False,
const=None,
_const=None,
init=None,
compact=False,
**kwds,
):
# const means add_impl_const, ie. declare current variable as constant (not pointed types)
# _const is the real const
align = first_not_None(align, self.align, False)
ctype = self.full_ctype(
align=align, multidecl=multidecl, add_impl_const=const, const=_const, **kwds
)
if (not multidecl) and len(ctype) == 0:
msg = f"Failed to get full ctype in {self.__class__}."
raise RuntimeError(msg)
# static array ctype needs to be split
name = self.decl_name()
if init is False:
msg = "Const variable should be initialized at declaration."
assert (not self.const) or (not self.add_impl_const), msg
init = None
else:
init = init if (init is not None) else self.init
if (len(ctype) > 0) and ctype[-1] == "*":
code = f"{ctype}${name}"
else:
code = f"{ctype} ${name}"
if init is not None:
if compact:
code = f"{code}={init}"
else:
code = f"{code} $= {init}"
elif self.known():
self.force_symbolic(False)
sval = self.sval()
if compact:
code = f"{code}={sval}"
else:
code = f"{code} $= {sval}"
if not multidecl:
code += ";"
self.force_symbolic()
self.declared = True
if not align:
code = code.replace("$", "")
if codegen is not None:
codegen.append(code)
return code.strip()
[docs]
def affect(self, codegen=None, align=None, init=None, compact=False, i=None):
align = first_not_None(align, self.align, False)
msg = "Cannot affect a const variable."
assert (not self.const) or (not self.add_impl_const), msg
init = self.init if (init is None) else init
if compact:
code = "{}$={};"
else:
code = "{} $= {};"
if i is None:
var = self
else:
var = self[i]
code = code.format(var, init)
if not align:
code = code.replace("$", "")
if codegen is not None:
codegen.append(code)
return code
@property
def dtype(self):
return ctype_to_dtype(self.ctype)
def __getitem__(self, ss):
if self.is_ptr:
return f"{self.name}[{ss}]"
elif ss == 0:
return self.__call__()
else:
assert self.is_ptr, f"{self.name} is not a pointer!"
def __repr__(self):
if self.is_symbolic():
return f"{self.name}({self.ctype})"
else:
return f"{self.name}({self.ctype},{self.value})"
def __call__(self):
return self.sval()
def __str__(self):
return self.sval()
[docs]
class CodegenArray(CodegenVariable):
@staticmethod
def _initialize_rec(
name,
typegen,
storage,
ctype,
const,
volatile,
shape,
sshape,
value,
svalue,
ptr_level,
ptr_restrict,
ptr_const,
ptr_volatile,
symbolic_mode,
):
if value is None:
return value, svalue
s0 = shape[0]
if ptr_level == 1:
_name = name
_value = value
if svalue is not None:
_svalue = "{ " + ", ".join(svalue) + " }"
else:
_shape = shape[1:]
_sshape = sshape[1:]
_ptr_level = ptr_level - 1
_ptr_const = ptr_const[:-1]
_ptr_restrict = ptr_restrict[:-1]
_ptr_volatile = ptr_volatile[:-1]
_value = [None] * s0
_svalue = [None] * s0
for d in range(s0):
_name = f"{name}_{d}"
dvalue = value[d]
dsvalue = svalue[d]
val, sval = CodegenArray._initialize_rec(
_name,
typegen,
storage,
ctype,
const,
volatile,
_shape,
_sshape,
dvalue,
dsvalue,
_ptr_level,
_ptr_restrict,
_ptr_const,
_ptr_volatile,
symbolic_mode,
)
var = CodegenArray(
name=_name,
typegen=typegen,
storage=storage,
ctype=ctype,
const=const,
volatile=volatile,
shape=_shape,
sshape=_sshape,
value=val,
svalue=sval,
dim=_ptr_level,
ptr_const=_ptr_const,
ptr_volatile=_ptr_volatile,
ptr_restrict=_ptr_restrict,
add_impl_const=False,
symbolic_mode=False,
struct_var=None,
_direct_init=True,
)
_value[d] = var
_svalue[d] = "\n\t".join(sval.split("\n"))
if svalue is None:
_svalue = None
else:
_svalue = "{\n\t" + ",\n\t".join(_svalue) + "\n}"
return _value, _svalue
def __init__(
self,
name,
ctype,
typegen,
storage=None,
volatile=False,
const=False,
add_impl_const=False,
dim=1,
ptr_const=None,
ptr_volatile=None,
ptr_restrict=None,
shape=None,
sshape=None,
value=None,
svalue=None,
symbolic_mode=False,
struct_var=None,
_direct_init=False,
):
ptr_level = dim
del dim
if _direct_init:
_value, _svalue = value, svalue
else:
ptr_const = [] if (ptr_const is None) else to_list(ptr_const)
ptr_volatile = [] if (ptr_volatile is None) else to_list(ptr_volatile)
ptr_restrict = [] if (ptr_restrict is None) else to_list(ptr_restrict)
ptr_const += [False] * (ptr_level - len(ptr_const))
ptr_volatile += [False] * (ptr_level - len(ptr_volatile))
ptr_restrict += [False] * (ptr_level - len(ptr_restrict))
if value is not None:
value = np.asarray(value)
if value.ndim != ptr_level:
raise ValueError("value array dim mismatch.")
if shape is None:
shape = value.shape
else:
assert value.shape == shape, "shape mismatch."
if svalue is not None:
svalue = np.asarray(svalue)
if value is None:
raise ValueError("value cannot be None when svalue is not None.")
if svalue.ndim != ptr_level:
raise ValueError("svalue input array dimension mismatch!")
if svalue.shape != value.shape:
raise ValueError(
"Input array shape mismatch between value and svalue."
)
elif value is not None:
svalue = np.zeros_like(value, dtype=object)
dtype = ctype_to_dtype(ctype)
sview = svalue.flat
for i, v in enumerate(value.flat):
sview[i] = typegen.dump(v)
if shape is not None:
if len(shape) != ptr_level:
raise ValueError("shape dim mismatch!")
else:
shape = (None,) * ptr_level
shape = np.asarray(shape)
if (sshape is None) and (shape[0] != None):
sshape = [str(s) for s in shape]
elif (sshape is not None) and len(sshape) != dim:
raise ValueError("sshape dim mismatch!")
else:
sshape = (None,) * ptr_level
sshape = np.asarray(sshape)
_value, _svalue = CodegenArray._initialize_rec(
name,
typegen,
storage,
ctype,
const,
volatile,
shape,
sshape,
value,
svalue,
ptr_level,
ptr_restrict,
ptr_const,
ptr_volatile,
symbolic_mode,
)
super().__init__(
name=name,
storage=storage,
ctype=ctype,
typegen=typegen,
value=_value,
svalue=_svalue,
const=const,
add_impl_const=add_impl_const,
volatile=volatile,
ptr=ptr_level,
ptr_restrict=ptr_restrict,
ptr_const=ptr_const,
ptr_volatile=ptr_volatile,
symbolic_mode=symbolic_mode,
struct_var=struct_var,
)
self.shape = shape
self.sshape = sshape
[docs]
def decl_name(self):
if self.shape is not None:
static_array = [f"[{val}]" for val in self.shape]
elif self.sshape is not None:
static_array = [f"[{val}]" for val in self.sshape]
else:
static_array = []
return "{}{}".format(self.name, "".join(static_array))
[docs]
def array_dim(self):
if self.shape is not None:
return self.shape.size
if self.sshape is not None:
return self.sshape.size
msg = "unknown array dim."
raise RuntimeError(msg)
[docs]
def ptr_ctype(self, impl=True, add_impl_const=None, cast=False):
if self.is_ptr:
add_impl_const = (
self.add_impl_const if (add_impl_const is None) else add_impl_const
)
dim = self.array_dim()
ptr_const = self.ptr_const[dim:]
ptr_volatile = self.ptr_volatile[dim:]
ptr_restrict = self.ptr_restrict[dim:]
ptrs = []
for i, (c, v, r) in enumerate(zip(ptr_const, ptr_volatile, ptr_restrict)):
if i == self.ptr_level - 1:
c = c or (impl and add_impl_const)
ptr = " $*{const}${volatile}${restrict}".format(
const="const " if (c and not cast) else "",
volatile="volatile " if (v and not cast) else "",
restrict="restrict " if (r and not cast) else "",
)
ptrs.append(ptr)
ptr_ctype = "".join(ptrs)
else:
ptr_ctype = ""
return ptr_ctype
[docs]
class CodegenVector(CodegenVariable):
def __init__(
self,
name,
ctype,
dim,
typegen,
value=None,
svalue=None,
storage=None,
const=False,
volatile=False,
ptr=False,
ptr_const=None,
ptr_volatile=None,
ptr_restrict=None,
add_impl_const=False,
nl=None,
symbolic_mode=False,
struct_var=None,
init=None,
):
super().__init__(
name=name,
ctype=ctype,
value=value,
typegen=typegen,
const=const,
volatile=volatile,
add_impl_const=add_impl_const,
storage=storage,
nl=nl,
ptr=ptr,
ptr_const=ptr_const,
ptr_volatile=ptr_volatile,
ptr_restrict=ptr_restrict,
symbolic_mode=symbolic_mode,
struct_var=struct_var,
init=init,
)
self.value = value
self._dim = dim
if value is not None:
self.svalue = svalue if svalue else [typegen.dump(v) for v in value]
else:
assert not svalue
self.svalue = None
@property
def dim(self):
return self._dim
[docs]
def sval(self, i=None):
if i is not None:
assert i < self.dim
if self.is_symbolic():
return f"{self.access_prefix(False)}{self.name}[{i}]"
else:
return self.svalue[i]
else:
if self.is_symbolic():
return self.access_prefix(False)
else:
expansion = ",".join(self.svalue)
return "{ " + expansion + " }"
def __getitem__(self, i):
return self.sval(i)
def __repr__(self):
if self.is_symbolic():
return f"{self.name}({self.ctype})"
else:
vals = "[" + ",".join(self.svalue) + "]"
return f"{self.name}({self.ctype},{vals})"
[docs]
class CodegenVectorClBuiltin(CodegenVector):
def __init__(
self,
name,
btype,
dim,
typegen,
value=None,
access_mode=None,
const=False,
add_impl_const=False,
storage=None,
nl=None,
init=None,
symbolic_mode=False,
struct_var=None,
**kwds,
):
factor = typegen.components(btype)
btype = typegen.basetype(btype)
dim *= factor
if dim > 1:
ctype = btype + str(dim)
access_mode = access_mode if access_mode else ("pos" if dim <= 4 else "hex")
msg = f"Wrong vector size {dim}"
assert dim in typegen.vsizes
msg = f"Invalid basetype {btype} for vector."
assert btype in (
typegen.float_base_types
+ typegen.signed_base_types
+ typegen.unsigned_base_types
), msg
msg = f"Invalid builtin type {ctype}."
assert ctype in typegen.builtin_types, ctype
else:
# scalar type
ctype = btype
access_mode = None
svalue = None
if value is not None:
dtype = ctype_to_dtype(btype)
value = np.asarray(value, dtype)
assert value.size == dim
svalue = [typegen.dump(np.asarray([f], dtype=dtype)[0]) for f in value]
super().__init__(
name=name,
ctype=ctype,
dim=dim,
typegen=typegen,
value=value,
svalue=svalue,
const=const,
add_impl_const=add_impl_const,
storage=storage,
nl=nl,
symbolic_mode=symbolic_mode,
struct_var=struct_var,
init=init,
)
self.btype = btype
self.access_mode = access_mode
[docs]
def newvar(self, name, btype=None, dim=None, **kwds):
btype = first_not_None(btype, self.btype)
dim = first_not_None(dim, self.dim)
return super().newvar(name=name, btype=btype, dim=dim, **kwds)
[docs]
def view(self, name, components, const=False):
if isinstance(components, slice):
start, stop, step = components.indices(self.dim)
it = tuple(range(start, stop, step))
dim = len(it)
elif components is Ellipsis:
it = tuple(range(self.dim))
dim = self.dim
else:
raise ValueError(
f"Unknown components type {components.__class__.__name__}."
)
if dim > self.dim:
raise ValueError("Dimension of view is greater than original vector!")
return CodegenVectorClBuiltin(
name=name,
btype=self.btype,
dim=dim,
typegen=self.typegen,
access_mode=self.access_mode,
const=self.const or const,
add_impl_const=self.add_impl_const,
storage=self.storage,
symbolic_mode=self.symbolic_mode,
value=self.value[components] if (self.value is not None) else None,
init=self[components],
)
[docs]
def set_value(self, value, svalue=None):
assert value is not None
value = to_list(value)
dtype = ctype_to_dtype(self.btype)
value = np.asarray(value, dtype).copy()
if value.size != self.dim:
raise ValueError("value dimension mismatch!")
if (self.value != value).any():
if self.known():
msg = f"Value was already set in variable {self.name}!"
raise RuntimeError(msg)
self.value = value
if svalue is not None:
self.svalue = svalue
else:
self.svalue = [self.typegen.dump(f) for f in value]
[docs]
def sval(self, i=None):
if self.is_symbolic():
if (i is not None) and (self.dim > 1):
return self.access_prefix(True) + self.typegen.vtype_access(
i, self.dim, self.access_mode
)
else:
return self.access_prefix(False)
else:
if i is not None:
return self.svalue[i]
else:
return self[:]
def __getitem__(self, key):
dim = self.dim
if isinstance(key, range):
key = tuple(key)
if isinstance(key, slice):
ids = tuple(range(*key.indices(dim)))
if self.declared and key.indices(dim) == (0, dim, 1):
return self.name
else:
return self.__getitem__(ids)
elif key is Ellipsis:
return self.__getitem__(slice(None))
elif isinstance(key, (tuple, list)):
if self.is_symbolic():
if self.dim > 1:
mode = self.access_mode
access = self.access_prefix(True)
access += "s" if mode.lower() == "hex" else ""
access += "".join(
[self.typegen.vtype_component_adressing(i, mode) for i in key]
)
else:
assert len(key) == 1 and key[0] == 0
access = self.access_prefix(False)
else:
ctype = self.btype + (str(len(key)) if len(key) != 1 else "")
value = [self.svalue[i] for i in key]
return "({})({})".format(ctype, ",".join(value))
return access
elif isinstance(key, (int, np.integer)):
if key < 0:
key += dim
if key < 0 or key >= dim:
raise IndexError(f"The index {key} is out of range.")
return self.sval(key)
else:
msg = f"Invalid key type {type(key)}!"
raise TypeError(msg)
[docs]
def declare(self, codegen=None, init=None, **kargs):
init = first_not_None(init, self.init)
if isinstance(init, int):
init = ",".join([self.typegen.dump(init) for _ in range(self.dim)])
init = f"({self.ctype})({init})"
elif init.__class__ in [list, tuple, np.ndarray]:
init = ",".join([self.typegen.dump(init[i]) for i in range(self.dim)])
init = f"({self.ctype})({init})"
return super().declare(init=init, codegen=codegen, **kargs)
[docs]
class CodegenVectorClBuiltinFunc(CodegenVectorClBuiltin):
def __init__(
self,
fname,
name,
btype,
dim,
typegen,
value=None,
access_mode="pos",
symbolic_mode=False,
const=False,
):
super().__init__(
name=name,
btype=btype,
dim=dim,
typegen=typegen,
value=value,
access_mode=access_mode,
const=const,
symbolic_mode=symbolic_mode,
)
self.fname = fname
[docs]
def fval(self, i=None):
if i is None:
value = [self.fval(i) for i in range(self.dim)]
return "({})({})".format(self.ctype, ",".join(value))
else:
assert i < self.dim
return f"get_{self.fname}({i})"
[docs]
def declare(self, codegen=None, init=None, **kargs):
if init is False:
init = None
else:
init = self.fval() if not self.known() else init
return super().declare(init=init, codegen=codegen, **kargs)
[docs]
class CodegenStruct(CodegenVariable):
def __init__(
self,
name,
struct,
storage=None,
const=False,
volatile=False,
ptr=False,
ptr_const=None,
ptr_volatile=None,
ptr_restrict=None,
add_impl_const=False,
nl=None,
symbolic_mode=False,
struct_var=None,
value=None,
var_overrides=None,
):
super().__init__(
name=name,
ctype=struct.ctype,
typegen=struct.typegen,
storage=storage,
const=const,
volatile=volatile,
ptr=ptr,
ptr_const=ptr_const,
ptr_volatile=ptr_volatile,
ptr_restrict=ptr_restrict,
add_impl_const=add_impl_const,
nl=nl,
value=value,
symbolic_mode=symbolic_mode,
struct_var=struct_var,
)
self.genvars(struct, var_overrides)
[docs]
def newvar(self, *args, **kwds):
kwds.setdefault("cls", CodegenVariable)
return super().newvar(*args, **kwds)
[docs]
def force_symbolic(self, force=True):
if force is None:
return
super().force_symbolic(force)
for k, var in self.vars.items():
var.force_symbolic(force)
def __getitem__(self, key):
if key in self.vars:
return self.vars[key]
else:
msg = "Unknown field {} in struct {}.\nAvailable fields are: {}"
msg = msg.format(key, self.name, self.vars.keys())
raise KeyError(msg)
def __getattr__(self, name):
if name in self.vars:
return self.__getitem__(name)
else:
raise AttributeError
[docs]
def genvars(self, struct, var_overrides):
if var_overrides is None:
var_overrides = {}
self.vars = VarDict()
struct_vars = re.compile(
r"\s+((?:struct\s+)?\w+)\s+((?:\s*\**(?:\w+)(?:\[\d+\])*[,;])+)"
)
var_decl = re.compile(r"(\**)(\w+)((?:\[\d+\])*)")
lines = struct.c_decl().split("\n")
svalue = []
for l in lines:
match = struct_vars.match(l)
if match:
ctype = match.group(1)
variables = match.group(2).replace(";", "").split(",")
for var in variables:
match = var_decl.match(var)
ptrs = match.group(1)
fieldname = match.group(2)
array = match.group(3)
nptrs = len(ptrs) if ptrs else 0
narray = (
[int(x) for x in array[:-1].replace("[", "").split("]")]
if array
else None
)
is_struct = struct.fields()[fieldname][0].fields is not None
if nptrs > 0:
raise RuntimeError(
"Pointer variables are not supported yet (opencl unsupported)!"
)
elif narray is not None:
raise RuntimeError(
"Static array variables are not supported yet!"
)
if self.value is not None:
if fieldname in var_overrides.keys():
pass
elif fieldname not in self.value.keys():
raise ValueError(
f"Value was given but field {fieldname} is not present."
)
else:
field_value = self.value[fieldname]
else:
field_value = None
if fieldname in var_overrides:
var = var_overrides[fieldname]
var.struct_var = self
var.const = self.const
elif (
ctype in hysop.backend.device.opencl.opencl_types.builtin_types
):
tg = struct.typegen
btype = tg.basetype(ctype)
dim = tg.components(ctype)
if (field_value is not None) and not isinstance(
field_value, np.ndarray
):
field_value = np.asarray([field_value], dtype=btype)
var = CodegenVectorClBuiltin(
name=fieldname,
btype=btype,
dim=dim,
access_mode=None,
const=self.const,
value=field_value,
typegen=struct.typegen,
struct_var=self,
)
else:
var = CodegenVariable(
name=fieldname,
ctype=ctype,
const=self.const,
value=field_value,
typegen=struct.typegen,
struct_var=self,
)
var.force_symbolic(False)
decl = var()
decl = decl.replace("\n", "\n\t")
sval = f".{fieldname} $= {decl}"
svalue.append(sval)
var.force_symbolic(self.symbolic_mode)
self.vars[fieldname] = var
if self.known():
self.svalue = "{\n\t" + ",\n\t".join(svalue) + "\n}"
def __getitem__(self, key):
return self.vars[key]
if __name__ == "__main__":
from hysop.backend.device.codegen.base.test import _test_typegen as test_typegen
tg = test_typegen("float")
print(":: ARRAYS ::")
var = CodegenArray(
name="A0",
ctype="float",
dim=3,
storage="__constant",
value=[
[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],
],
typegen=tg,
)
print(var.declare())
# runtime known variable
print(":: SYMBOLIC VECTOR ::")
var = CodegenVectorClBuiltin("gid", "int", 3, tg)
print(var())
for i in range(var.dim):
print(var[i])
print(var[:])
print(var.declare())
print()
# change access mode
print(":: ACCESS MODES ::")
var = CodegenVectorClBuiltin("ids", "int", 4, tg, access_mode="pos")
print(var[::2])
var = CodegenVectorClBuiltin("ids", "int", 16, tg, access_mode="hex")
print(var[::2])
var = CodegenVectorClBuiltin("ids", "int", 16, tg, access_mode="HEX")
print(var[::2])
print()
# compilation time known variable
print(":: KNOWN VECTOR ::")
var = CodegenVectorClBuiltin("lid", "int", 3, tg, value=(256, 512, 1024))
print(var())
for i in range(var.dim):
print(var[i])
print(var[1:3])
print(var.declare(const=True))
print()
# force the use of symbolic value
print(":: FORCE SYMBOLIC ACCESS ::")
var = CodegenVectorClBuiltin("id", "int", 4, tg, value=(1, 2, 3, 4))
print(var.declare())
var.force_symbolic()
print(var[:])
var.force_symbolic(False)
print(var[:])
print()
# default decimal float dumper
print(":: DEFAULT FLOAT DUMPER ::")
var = CodegenVectorClBuiltin("size", "float", 4, tg, value=(1.0, 2.0, 4.0, 8.0))
print(var())
for i in range(var.dim):
print(var[i])
print(var[1:3])
print(var.declare(storage="__constant"))
print()
# hexadecimal deciml float dumper
print(":: HEXADECIMAL FLOAT DUMPER ::")
var = CodegenVectorClBuiltin(
"size_hex",
"float",
4,
value=(1.0, 2.0, 4.0, 8.0),
typegen=test_typegen("float", float_dump_mode="hex"),
)
print(var())
for i in range(var.dim):
print(var[i])
print(var[1:3])
print(var.declare())
print()
# bultin opencl functions
for fname, name in [
("global_size", "gsize"),
("local_size", "lsize"),
("global_id", "gid"),
("local_id", "lid"),
]:
var = CodegenVectorClBuiltinFunc(fname, name, "int", 3, tg)
print(var.declare())